/************************************************
 * MirrorOS Kernel Drivers
 * Fat FS Driver
 * Copyright 2008 Matthew Iselin
 ***********************************************/

// Defines

// debugging
#define DEBUG 0

// seeking stuff
#define SEEK_SET		0
#define SEEK_CUR		1
#define SEEK_END		2

// which FS to use
#define FATFS_32		1
#define FATFS_16		2
#define FATFS_12		3

// directory entry flags
#define ATTR_READONLY		0x01
#define ATTR_HIDDEN			0x02
#define ATTR_SYSTEM			0x04
#define ATTR_VOLUME_ID		0x08
#define ATTR_DIRECTORY		0x10
#define ATTR_ARCHIVE		0x20
#define ATTR_LONG_NAME		( ATTR_sys_read_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID )
#define ATTR_LONG_NAME_MASK	( ATTR_sys_read_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID | ATTR_DIRECTORY | ATTR_ARCHIVE )

// long directory entry defines
#define LAST_LONG_ENTRY		0x40

// st_mode bitmasks
#define 	_IFMT	0170000	/* type of file */
#define		_IFDIR	0040000	/* directory */
#define		_IFCHR	0020000	/* character special */
#define		_IFBLK	0060000	/* block special */
#define		_IFREG	0100000	/* regular */
#define		_IFLNK	0120000	/* symbolic link */
#define		_IFSOCK	0140000	/* socket */
#define		_IFIFO	0010000	/* fifo */

/** ALL THE FOLLOWING DEFINES ARE FROM THE NEWLIB sys/fcntl.h FILE - THEY DEFINE FLAGS USED FOR OPEN() CALLS **/

#define	_FOPEN		(-1)	/* from sys/file.h, kernel use only */
#define	_FREAD		0x0001	/* read enabled */
#define	_FWRITE		0x0002	/* write enabled */
#define	_FAPPEND	0x0008	/* append (writes guaranteed at the end) */
#define	_FMARK		0x0010	/* internal; mark during gc() */
#define	_FDEFER		0x0020	/* internal; defer for next gc pass */
#define	_FASYNC		0x0040	/* signal pgrp when data ready */
#define	_FSHLOCK	0x0080	/* BSD flock() shared lock present */
#define	_FEXLOCK	0x0100	/* BSD flock() exclusive lock present */
#define	_FCREAT		0x0200	/* open with file create */
#define	_FTRUNC		0x0400	/* open with truncation */
#define	_FEXCL		0x0800	/* error on open if file exists */
#define	_FNBIO		0x1000	/* non blocking I/O (sys5 style) */
#define	_FSYNC		0x2000	/* do all writes synchronously */
#define	_FNONBLOCK	0x4000	/* non blocking I/O (POSIX style) */
#define	_FNDELAY	_FNONBLOCK	/* non blocking I/O (4.2 style) */
#define	_FNOCTTY	0x8000	/* don't assign a ctty on this open */

#define	O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)

/*
 * Flag values for open(2) and fcntl(2)
 * The kernel adds 1 to the open modes to turn it into some
 * combination of FREAD and FWRITE.
 */
#define	O_RDONLY	0		/* +1 == FREAD */
#define	O_WRONLY	1		/* +1 == FWRITE */
#define	O_RDWR		2		/* +1 == FREAD|FWRITE */
#define	O_APPEND	_FAPPEND
#define	O_CREAT		_FCREAT
#define	O_TRUNC		_FTRUNC
#define	O_EXCL		_FEXCL
#define O_SYNC		_FSYNC
/*	O_NDELAY	_FNDELAY 	set in include/fcntl.h */
/*	O_NDELAY	_FNBIO 		set in include/fcntl.h */
#define	O_NONBLOCK	_FNONBLOCK
#define	O_NOCTTY	_FNOCTTY

// Types

// struct stat stuff
#ifdef __CYGWIN__
#include <cygwin/stat.h>
#ifdef _COMPILING_NEWLIB
#define stat64 __stat64
#endif
#else
struct	stat
{
  short		st_dev;
  unsigned short		st_ino;
  unsigned int	st_mode;
  unsigned short	st_nlink;
  unsigned short		st_uid;
  unsigned short		st_gid;
  short		st_rdev;
  long		st_size;
  /* SysV/sco doesn't have the rest... But Solaris, eabi does.  */
#if defined(__svr4__) && !defined(__PPC__) && !defined(__sun__)
  unsigned long	st_atime;
  unsigned long	st_mtime;
  unsigned long	st_ctime;
#else
  unsigned long	st_atime;
  long		st_spare1;
  unsigned long	st_mtime;
  long		st_spare2;
  unsigned long	st_ctime;
  long		st_spare3;
  long		st_blksize;
  long		st_blocks;
  long	st_spare4[2];
#endif
};
#endif

// boot parameter block for all FAT filesystems
struct BPB_FAT
{
	unsigned char	BS_jmpBoot[3];
	unsigned char	BS_OEMName[8];
	unsigned short	BPB_BytsPerSec;
	unsigned char	BPB_SecPerClus;
	unsigned short	BPB_RsvdSecCnt;
	unsigned char	BPB_NumFATs;
	unsigned short	BPB_RootEntCnt;
	unsigned short	BPB_TotSec16;
	unsigned char	BPB_Media;
	unsigned short	BPB_FATSz16;
	unsigned short	BPB_SecPerTrk;
	unsigned short	BPB_NumHeads;
	unsigned int	BPB_HiddSec;
	unsigned int	BPB_TotSec32;
} __attribute((packed));

// special fat32 BPB starting at offset 36 of the bootsector
struct BPB_FAT32
{
	unsigned int	BPB_FATSz32;
	unsigned short	BPB_ExtFlags;
	unsigned short	BPB_FsVer;
	unsigned int	BPB_RootClus;
	unsigned short	BPB_FsInfo;
	unsigned short	BPB_BkBootSec;
	unsigned char	BPB_Reserved[12];
	unsigned char	BS_DrvNum;
	unsigned char	BS_Reserved1;
	unsigned char	BS_BootSig;
	unsigned int	BS_VolID;
	unsigned char	BS_VolLab[11];
	unsigned char	BS_FilSysType[8];
} __attribute((packed));

// special FAT16/12 BPB starting at offset 36 of the bootsector
struct BPB_FAT16_12
{
	unsigned char	BS_DrvNum; // drive number (the BIOS int 0x13 one) - OS specific
	unsigned char	BS_Reserved1; // reserved for use by WinNT
	unsigned char	BS_BootSig; // 0x29 signature byte (if 0x29, next three fields are present and valid)
	unsigned int	BS_VolID; // volume serial number
	char			BS_VolLab[11]; // volume label
	char			BS_FilSysType[8]; // filesystem string, not used to check type of FS
} __attribute((packed));

// a FAT directory entry
struct FAT_DirEnt {
	unsigned char	DIR_Name[11];
	unsigned char	DIR_Attr;
	unsigned char	DIR_NTRes;
	unsigned char	DIR_CrtTimeTenth;
	unsigned short	DIR_CrtTime;
	unsigned short	DIR_CrtDate;
	unsigned short	DIR_LstAccDate;
	unsigned short	DIR_FstClusHI;
	unsigned short	DIR_WrtTime;
	unsigned short	DIR_WrtDate;
	unsigned short	DIR_FstClusLO;
	unsigned int	DIR_FileSize;
};

// the FAT32 FSInfo structure and backup bootsector
struct FAT_FSINFO32 {
	unsigned int	FSI_LeadSig; // always 0x41615252
	char			FSI_Reserved1[480];
	unsigned int	FSI_StrucSig; // always 0x61417272
	unsigned int	FSI_Free_Count;
	unsigned int	FSI_Nxt_Free;
	char			FSI_Reserved2[12];
	unsigned int	FSI_TrailSig; // always 0xAA550000
};

// a FAT filesystem
struct fat_fs {
	int valid; // whether we can use this filesystem
	char mount[32]; // the mount point, basically what you should call open() with
	char fsmount[32]; // the mount point of the FS, basically the root directory path
	int desc; // descriptor for I/O on this filesystem
	int fstype; // FATFS_* entries
	struct BPB_FAT bpb; // boot parameter block
	struct BPB_FAT32 bpb32; // only valid for FAT32 filesystems
	struct FAT_FSINFO32 fsinfo; // only valid for FAT32 filesystems
	struct BPB_FAT16_12 bpb16_12; // only valid for FAT16/12 filesystems
	
	unsigned int data_area_start; // the start of the data area
	unsigned int root_dir_count; // number of sectors that the root directory uses
};

// an open file
struct intdesc {
	int valid; // whether this is a valid descriptor or not
	int fsnum; // this is used to get the BPB, filesystem type, and mount point
	int desc; // the descriptor used for this file (ie, a descriptor for a drive)
	int isfile; // whether the file is actually a file or a directory
	int openflags; // flags passed to open
	unsigned int dir_sector; // the sector in which the directory entry for this file can be found
	unsigned int dir_offset; // the index into the above sector of the directory entry
	struct FAT_DirEnt dir_entry; // the directory entry itself
	
	unsigned long desc_offset; // the offset in the file that this descriptor works for
};

// a directory entry (dirent.h style)
struct dirent {
	char			d_name[1024]; // the filename
	unsigned long	d_off; // offset into the directory on the FS
	unsigned short	d_namlen; // length of the filename in d_name
	unsigned short	d_reclen; // unknown
	unsigned short	d_ino; // file serial number
} __attribute__((packed));

// Prototypes

// sends debug information
int dprintf( const char* fmt, ... );

// installs a device
void InstallDevice(	char* prefix,
					unsigned int s_open,
					unsigned int s_read,
					unsigned int s_write,
					unsigned int s_seek,
					unsigned int s_ioctl,
					unsigned int s_stat,
					unsigned int s_close,
					int blocksize
					);

// reads in data from a file
int fat_internal_sys_read_file( struct fat_fs fs, struct intdesc desc, char* dest, int count );

// writes data to a file
int fat_internal_sys_write_file( struct fat_fs fs, struct intdesc desc, char* buff, int count );

// gets the first sector of a cluster
unsigned int first_sector_of_cluster( struct fat_fs fs, unsigned int clus );

// finds a free cluster in the FAT
unsigned int find_free_clus( struct fat_fs fs );

// gets the data for a specific cluster in the FAT (NOT the next cluster, the DATA in the FAT for that entry)
unsigned int get_clus_entry( struct fat_fs fs, unsigned int clus );

// sets the data for a specific cluster in the FAT
unsigned int set_clus_entry( struct fat_fs fs, unsigned int clus, unsigned int value );

// converts a string to 8.3 format
char* convert_to_8_3( char* str );
